﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using SFP.Audio;
using SFP.Input;
using SFP.CollisionDetection;

namespace SFP
{
    /* Extends the Form class which describes a window where the game is rendered onto.
     * */
    class SFPForm : Form
    {
        // References the Game object.
        private Game game;
        // A high performance timer that can take time on the level of nanoseconds.
        private QueryPerfCounter timer;
        // All rendered frames are drawed onto a PictureBox which has functionality regarding
        // image operations like stretching the image.
        private PictureBox pictureBox;
        // Describes the current Frame. All render operations are executed on its Graphics object.
        private Bitmap currentFrame;
        // Determines weather the game is halted or not.
        private bool gameHalted = true;
        // The time taken for rendering a frame in nanoseconds.
        private double time = 0;
        // The amount of frames rendered during one second.
        private double FPS = 0;
        // The time in nanoseconds left until the FPS will be updated.
        private double FPSUpdateTime = 0;

        /* The constructor calls initial things for setting up the form. Specific handlers are
         * also registered here.
         * */
        public SFPForm(Game game)
        {
            this.game = game;
            this.timer = new QueryPerfCounter();
            this.pictureBox = new PictureBox();
            this.currentFrame = new Bitmap(game.resolution.Width, game.resolution.Height, PixelFormat.Format32bppPArgb);

            Mouse.form = this;

            // Setup the form.
            Text = "Game";
            ClientSize = game.resolution;
            StartPosition = FormStartPosition.WindowsDefaultLocation;
            FormBorderStyle = FormBorderStyle.FixedSingle;
            MaximizeBox = false;

            // Register handlers to the form.
            FormClosing += new FormClosingEventHandler(SFPForm_FormClosing);
            Resize += new EventHandler(SFPForm_Resize);

            // Set up the PictureBox and assign the bitmap for the rendered frame to it.
            pictureBox.Size = ClientSize;
            pictureBox.BackColor = Color.Black;
            pictureBox.SizeMode = PictureBoxSizeMode.CenterImage;
            pictureBox.Image = currentFrame;

            // Register handlers to the PictureBox.
            pictureBox.Paint += new PaintEventHandler(pictureBox_Paint);
            pictureBox.MouseDown += new MouseEventHandler(Mouse.HandleMouseDown);
            pictureBox.MouseUp += new MouseEventHandler(Mouse.HandleMouseUp);

            // Add the picturebox to the form's conrtols.
            Controls.Add(pictureBox);
        }

        private void SFPForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (game.forbidClosing)
            {
                e.Cancel = true;
                return;
            }

            game.FormClosingTasks();
        }

        private void SFPForm_Resize(object sender, EventArgs e)
        {
            pictureBox.Size = ClientSize;
        }

        internal PictureBox getPictureBox()
        {
            return pictureBox;
        }

        /* This method is called when the picture box is invalidated, which means that the content has to be
         * updated and thus be redrawed. Because the picture box is invalidated just at the end of this method
         * it will be updated immediatly and that is how the render loop works.
         * */
        private void pictureBox_Paint(object sender, PaintEventArgs e)
        {
            // First frame calculated, no time taken so far OR game is halted
            if (!gameHalted)
            {
                // Take time since the current frame started to be calculated
                timer.Stop();
                time = timer.Duration(1);

                // Limit frame rate
                if (game.limitFrameRate)
                {
                    while (time < 1000000000 / (double)game.maxFrameRate)
                    {
                        timer.Stop();
                        time = timer.Duration(1);
                    }
                }
            }

            // -----------------------------------------------------------------
            // Start to calculate the next frame
            timer.Start();

            Graphics graphics = Graphics.FromImage(currentFrame);

            if (this == Form.ActiveForm || !game.pauseGameOnInactiveForm)
            {
                gameHalted = false;
                calculateNextFrame(graphics);
                Collision.clean();
                updateAudioPlayer();
            }
            else
            {
                gameHalted = true;
            }

            drawFPS(graphics);

            // Invalidate the game scence
            pictureBox.Invalidate();
        }

        private void calculateNextFrame(Graphics graphics)
        {
            // Update the game scene ('time' is passed in milliseconds)
            game.UpdateGame(time / 1000000);

            // Render the next frame
            graphics.Clear(game.bgColor);
            game.DrawGame(graphics);
        }

        /* Draws the current FPS on the upper left corner of the form.
         * */
        private void drawFPS(Graphics graphics)
        {
            if (game.showFPS)
            {
                if (gameHalted)
                {
                    FPS = 0;
                    FPSUpdateTime = 500000000;
                }
                else if (FPSUpdateTime >= 500000000)
                {
                    FPS = Math.Round(1000000000 / time, 2, MidpointRounding.AwayFromZero);
                    FPSUpdateTime = 0;
                }
                graphics.FillRectangle(new SolidBrush(Color.Black), 5, 5, 140, 22);
                graphics.DrawString("FPS: " + FPS, new Font("Arial", 16), new SolidBrush(Color.Yellow), new PointF(5, 5));
                FPSUpdateTime += time;
            }
        }

        /* Clean up ended audio plays or loop them if they are registered for looping.
         * */
        private void updateAudioPlayer()
        {
            AudioPlayer.clean();
            AudioPlayer.checkLoop();
        }
    }
}
